/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "zlang_in.h"

int CheckingFunction( struct ZlangRuntime *rt , struct ZlangObject *master_obj , struct ZlangFunction *func )
{
	struct ZlangObjectsStackFrame			*old_local_objs_stack_frame = NULL ;
	int						old_local_objs_stack_frame_curridx ;
	struct ZlangObjectsStackFrame			*old_tmp_objs_stack_frame = NULL ;
	int						old_tmp_objs_stack_frame_curridx ;
	struct ZlangDefersStackFrame			*old_defers_stack_frame = NULL ;
	int						old_defers_stack_frame_curridx ;
	struct ZlangObjectsStackFrame			*new_local_objs_stack_frame = NULL ;
	int						new_local_objs_stack_frame_curridx ;
	struct ZlangObjectsStackFrame			*new_tmp_objs_stack_frame = NULL ;
	int						new_tmp_objs_stack_frame_curridx ;
	struct ZlangDefersStackFrame			*new_defers_stack_frame = NULL ;
	int						new_defers_stack_frame_curridx ;
	
	struct ZlangFunctionParameter			*in_param = NULL ;
	struct ZlangObject				*in_parent_obj = NULL ;
	struct ZlangObject				*in_param_obj = NULL ;
	struct ZlangObject				*out_parent_obj = NULL ;
	struct ZlangObject				*out_param_obj = NULL ;

	struct ZlangObject				*restore_in_obj = NULL ;
	struct ZlangFunction				*restore_in_func = NULL ;
	struct ZlangTokenDataPageHeader			*restore_token_datapage_header = NULL ;
	char						*restore_token_dataunit = NULL ;
	struct ZlangTokenDataUnitHeader			*restore_token_info = NULL ;
	char						*restore_token = NULL ;
	
	int						nret = 0 ;

	TEST_RUNTIME_DEBUG_THEN_PRINT_ENTER_FUNCTION(rt)
	
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CheckingFunction begin , master_obj[%s] func_name[%s]" , GetObjectName(master_obj) , GetFunctionName(func) )
	
	if( func->invoke_func )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "this is a C Function" )
		TEST_RUNTIME_DEBUG_THEN_PRINT_LEAVE_FUNCTION(rt)
		return ZLANG_INFO_RETURN;
	}
	
	if( func->func_begin_token_datapage_header == NULL && func->func_begin_token_dataunit == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "this is a virtual function in interface" )
		TEST_RUNTIME_DEBUG_THEN_PRINT_LEAVE_FUNCTION(rt)
		return ZLANG_INFO_RETURN;
	}
	
	old_local_objs_stack_frame = GetCurrentLocalObjectsStackFrame( rt ) ;
	old_local_objs_stack_frame_curridx = GetCurrentLocalObjectsStackFrameIndex( rt ) ;
	old_tmp_objs_stack_frame = GetCurrentTmpObjectsStackFrame( rt ) ;
	old_tmp_objs_stack_frame_curridx = GetCurrentTmpObjectsStackFrameIndex( rt ) ;
	old_defers_stack_frame = GetCurrentDefersStackFrame( rt ) ;
	old_defers_stack_frame_curridx = GetCurrentDefersStackFrameIndex( rt ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "backup local_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , old_local_objs_stack_frame_curridx,old_local_objs_stack_frame->full_func_name , old_local_objs_stack_frame->stack_bottom,old_local_objs_stack_frame->stack_in_params_top,old_local_objs_stack_frame->stack_out_params_top,old_local_objs_stack_frame->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "backup tmp_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , old_tmp_objs_stack_frame_curridx,old_tmp_objs_stack_frame->full_func_name , old_tmp_objs_stack_frame->stack_bottom,old_tmp_objs_stack_frame->stack_in_params_top,old_tmp_objs_stack_frame->stack_out_params_top,old_tmp_objs_stack_frame->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "backup defers_stack_frame[%d][%s]-[%d][%d]" , old_defers_stack_frame_curridx,old_defers_stack_frame->full_func_name , old_defers_stack_frame->stack_bottom,old_defers_stack_frame->stack_top )
	
	IncreaseStackFrame( rt , func->func_name ) ;
	new_local_objs_stack_frame = GetCurrentLocalObjectsStackFrame( rt ) ;
	new_local_objs_stack_frame_curridx = GetCurrentLocalObjectsStackFrameIndex( rt ) ;
	new_tmp_objs_stack_frame = GetCurrentTmpObjectsStackFrame( rt ) ;
	new_tmp_objs_stack_frame_curridx = GetCurrentTmpObjectsStackFrameIndex( rt ) ;
	new_defers_stack_frame = GetCurrentDefersStackFrame( rt ) ;
	new_defers_stack_frame_curridx = GetCurrentDefersStackFrameIndex( rt ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "new local_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , new_local_objs_stack_frame_curridx,new_local_objs_stack_frame->full_func_name , new_local_objs_stack_frame->stack_bottom,new_local_objs_stack_frame->stack_in_params_top,new_local_objs_stack_frame->stack_out_params_top,new_local_objs_stack_frame->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "new tmp_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , new_tmp_objs_stack_frame_curridx,new_tmp_objs_stack_frame->full_func_name , new_tmp_objs_stack_frame->stack_bottom,new_tmp_objs_stack_frame->stack_in_params_top,new_tmp_objs_stack_frame->stack_out_params_top,new_tmp_objs_stack_frame->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "new defers_stack_frame[%d][%s]-[%d][%d]" , new_defers_stack_frame_curridx,new_defers_stack_frame->full_func_name , new_defers_stack_frame->stack_bottom,new_defers_stack_frame->stack_top )
	
	in_param = NULL ;
	for( ; ; )
	{
		in_param = TravelFunctionInputParameter( rt , func , in_param ) ;
		if( in_param == NULL )
			break;

		if( STRCMP( in_param->parent_obj_name , == , ZLANG_OBJECT_vargs ) )
			continue;
		
		if( STRCMP( in_param->parent_obj_name , == , ZLANG_OBJECT_object ) )
		{
			in_param_obj = CloneObjectInLocalStack( rt , in_param->obj_name , NULL ) ;
			if( in_param_obj == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObjectInLocalStack[%s] failed" , in_param->obj_name )
				TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
				return GetRuntimeErrorNo(rt);
			}
		}
		else
		{
			in_parent_obj = QueryGlobalObjectByObjectName( rt , in_param->parent_obj_name ) ;
			if( in_parent_obj == NULL )
			{
				SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_OBJECT_NOT_IMPORTED_OR_DECLARED , "object '%s' not imported or declared" , in_param->parent_obj_name )
				TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
				return ZLANG_ERROR_OBJECT_NOT_IMPORTED_OR_DECLARED;
			}
				
			in_param_obj = CloneObjectInLocalStack( rt , in_param->obj_name , in_parent_obj ) ;
			if( in_param_obj == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObjectInLocalStack[%s][%s] failed" , in_param->obj_name , GetObjectName(in_parent_obj) )
				TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
				return GetRuntimeErrorNo(rt);
			}
		}
	}

	MarkInputParamtersTop( new_local_objs_stack_frame );

	if( func->out_param && STRCMP( func->out_param->parent_obj_name , != , ZLANG_OBJECT_void ) )
	{
		out_parent_obj = QueryGlobalObjectByObjectName( rt , func->out_param->parent_obj_name ) ;
		if( out_parent_obj == NULL )
		{
			SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_OBJECT_NOT_IMPORTED_OR_DECLARED , "object '%s' not imported or declared" , func->out_param->parent_obj_name )
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return ZLANG_ERROR_OBJECT_NOT_IMPORTED_OR_DECLARED;
		}
			
		out_param_obj = CloneObjectInLocalStack( rt , func->out_param->obj_name , out_parent_obj ) ;
		if( out_param_obj == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneObjectInLocalStack[%s][%s] failed" , func->out_param->obj_name , GetObjectName(out_parent_obj) )
			TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
			return GetRuntimeErrorNo(rt);
		}
	}

	MarkOutputParamtersTop( new_local_objs_stack_frame );

	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CALL InterpretStatementSegment" )
	restore_in_obj = rt->in_obj ;
	restore_in_func = rt->in_func ;
	restore_token_datapage_header = rt->travel_token_datapage_header ;
	restore_token_dataunit = rt->travel_token_dataunit ;
	restore_token_info = rt->travel_token_info ;
	restore_token = rt->travel_token ;
	rt->in_obj = master_obj ;
	rt->in_func = func ;
	rt->travel_token_datapage_header = func->func_begin_token_datapage_header ;
	rt->travel_token_dataunit = func->func_begin_token_dataunit ;
	nret = InterpretStatementSegment( rt , NULL ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "InterpretStatementSegment return[%d]" , nret )
	rt->in_obj = restore_in_obj ;
	rt->in_func = restore_in_func ;
	rt->travel_token_datapage_header = restore_token_datapage_header ;
	rt->travel_token_dataunit = restore_token_dataunit ;
	rt->travel_token_info = restore_token_info ;
	rt->travel_token = restore_token ;
	if( nret != ZLANG_INFO_END_OF_STATEMENT_SEGMENT && nret != ZLANG_INFO_RETURN && nret != ZLANG_INFO_EXIT )
	{
		DecreaseStackFrame( rt );
		old_local_objs_stack_frame = GetCurrentLocalObjectsStackFrame( rt ) ;
		old_local_objs_stack_frame_curridx = GetCurrentLocalObjectsStackFrameIndex( rt ) ;
		old_tmp_objs_stack_frame = GetCurrentTmpObjectsStackFrame( rt ) ;
		old_tmp_objs_stack_frame_curridx = GetCurrentTmpObjectsStackFrameIndex( rt ) ;
		old_defers_stack_frame = GetCurrentDefersStackFrame( rt ) ;
		old_defers_stack_frame_curridx = GetCurrentDefersStackFrameIndex( rt ) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore local_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , old_local_objs_stack_frame_curridx,old_local_objs_stack_frame->full_func_name , old_local_objs_stack_frame->stack_bottom,old_local_objs_stack_frame->stack_in_params_top,old_local_objs_stack_frame->stack_out_params_top,old_local_objs_stack_frame->stack_local_var_top )
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore tmp_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , old_tmp_objs_stack_frame_curridx,old_tmp_objs_stack_frame->full_func_name , old_tmp_objs_stack_frame->stack_bottom,old_tmp_objs_stack_frame->stack_in_params_top,old_tmp_objs_stack_frame->stack_out_params_top,old_tmp_objs_stack_frame->stack_local_var_top )
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore defers_stack_frame[%d][%s]-[%d][%d]" , old_defers_stack_frame_curridx,old_defers_stack_frame->full_func_name , old_defers_stack_frame->stack_bottom,old_defers_stack_frame->stack_top )

		TEST_RUNTIME_DEBUG_THEN_PRINT_INTERRUPT_FUNCTION(rt)
		return nret;
	}

	DecreaseStackFrame( rt );
	old_local_objs_stack_frame = GetCurrentLocalObjectsStackFrame( rt ) ;
	old_local_objs_stack_frame_curridx = GetCurrentLocalObjectsStackFrameIndex( rt ) ;
	old_tmp_objs_stack_frame = GetCurrentTmpObjectsStackFrame( rt ) ;
	old_tmp_objs_stack_frame_curridx = GetCurrentTmpObjectsStackFrameIndex( rt ) ;
	old_defers_stack_frame = GetCurrentDefersStackFrame( rt ) ;
	old_defers_stack_frame_curridx = GetCurrentDefersStackFrameIndex( rt ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore local_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , old_local_objs_stack_frame_curridx,old_local_objs_stack_frame->full_func_name , old_local_objs_stack_frame->stack_bottom,old_local_objs_stack_frame->stack_in_params_top,old_local_objs_stack_frame->stack_out_params_top,old_local_objs_stack_frame->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore tmp_objs_stack_frame[%d][%s]-[%d][%d][%d][%d]" , old_tmp_objs_stack_frame_curridx,old_tmp_objs_stack_frame->full_func_name , old_tmp_objs_stack_frame->stack_bottom,old_tmp_objs_stack_frame->stack_in_params_top,old_tmp_objs_stack_frame->stack_out_params_top,old_tmp_objs_stack_frame->stack_local_var_top )
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "restore defers_stack_frame[%d][%s]-[%d][%d]" , old_defers_stack_frame_curridx,old_defers_stack_frame->full_func_name , old_defers_stack_frame->stack_bottom,old_defers_stack_frame->stack_top )

	TEST_RUNTIME_DEBUG_THEN_PRINT_LEAVE_FUNCTION(rt)
	return nret;
}
